css: Fix text-decoration-line support
authorMatthias Clasen <mclasen@redhat.com>
Tue, 26 Jan 2021 22:40:34 +0000 (17:40 -0500)
committerMatthias Clasen <mclasen@redhat.com>
Tue, 26 Jan 2021 22:53:14 +0000 (17:53 -0500)
This property needs to be treated as flags, not as
enum, since it should be possible to specify more
than one value, e.g.

text-decoration-line: underline overline;

Tests included.

Fixes: #3621
gtk/gtkcssenumvalue.c
gtk/gtkcssenumvalueprivate.h
gtk/gtkcssshorthandpropertyimpl.c
gtk/gtkcssstyle.c
gtk/gtkcssstylepropertyimpl.c
gtk/gtkcsstypesprivate.h
testsuite/css/parser/meson.build
testsuite/css/parser/text-decoration-line.css
testsuite/css/parser/text-decoration-line.ref.css
testsuite/css/parser/text-decoration.css [new file with mode: 0644]
testsuite/css/parser/text-decoration.ref.css [new file with mode: 0644]

index 6a9b4bfec7cc74566480d41695530194070ef070..d28daf5ac2feb97f3b7e016648964102d854eac7 100644 (file)
@@ -545,58 +545,6 @@ _gtk_css_font_stretch_value_get (const GtkCssValue *value)
   return value->value;
 }
 
-/* GtkTextDecorationLine */
-
-static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_LINE = {
-  "GtkCssTextDecorationLineValue",
-  gtk_css_value_enum_free,
-  gtk_css_value_enum_compute,
-  gtk_css_value_enum_equal,
-  gtk_css_value_enum_transition,
-  NULL,
-  NULL,
-  gtk_css_value_enum_print
-};
-
-static GtkCssValue text_decoration_line_values[] = {
-  { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_NONE, "none" },
-  { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE, "underline" },
-  { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_OVERLINE, "overline" },
-  { &GTK_CSS_VALUE_TEXT_DECORATION_LINE, 1, TRUE, GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH, "line-through" },
-};
-
-GtkCssValue *
-_gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line)
-{
-  g_return_val_if_fail (line < G_N_ELEMENTS (text_decoration_line_values), NULL);
-
-  return _gtk_css_value_ref (&text_decoration_line_values[line]);
-}
-
-GtkCssValue *
-_gtk_css_text_decoration_line_value_try_parse (GtkCssParser *parser)
-{
-  guint i;
-
-  g_return_val_if_fail (parser != NULL, NULL);
-
-  for (i = 0; i < G_N_ELEMENTS (text_decoration_line_values); i++)
-    {
-      if (gtk_css_parser_try_ident (parser, text_decoration_line_values[i].name))
-        return _gtk_css_value_ref (&text_decoration_line_values[i]);
-    }
-
-  return NULL;
-}
-
-GtkTextDecorationLine
-_gtk_css_text_decoration_line_value_get (const GtkCssValue *value)
-{
-  g_return_val_if_fail (value->class == &GTK_CSS_VALUE_TEXT_DECORATION_LINE, GTK_CSS_TEXT_DECORATION_LINE_NONE);
-
-  return value->value;
-}
-
 /* GtkTextDecorationStyle */
 
 static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_STYLE = {
@@ -1204,6 +1152,99 @@ gtk_css_value_flags_print (const FlagsValue  *values,
     }
 }
 
+/* GtkTextDecorationLine */
+
+static FlagsValue text_decoration_line_values[] = {
+  { GTK_CSS_TEXT_DECORATION_LINE_NONE, "none" },
+  { GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE, "underline" },
+  { GTK_CSS_TEXT_DECORATION_LINE_OVERLINE, "overline" },
+  { GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH, "line-through" },
+};
+
+static void
+gtk_css_text_decoration_line_value_print (const GtkCssValue *value,
+                                          GString           *string)
+{
+  gtk_css_value_flags_print (text_decoration_line_values,
+                             G_N_ELEMENTS (text_decoration_line_values),
+                             value, string);
+}
+
+static const GtkCssValueClass GTK_CSS_VALUE_TEXT_DECORATION_LINE = {
+  "GtkCssTextDecorationLine",
+  gtk_css_value_enum_free,
+  gtk_css_value_enum_compute,
+  gtk_css_value_flags_equal,
+  gtk_css_value_enum_transition,
+  NULL,
+  NULL,
+  gtk_css_text_decoration_line_value_print
+};
+
+static gboolean
+text_decoration_line_is_valid (GtkTextDecorationLine line)
+{
+  if ((line & GTK_CSS_TEXT_DECORATION_LINE_NONE) &&
+      (line != GTK_CSS_TEXT_DECORATION_LINE_NONE))
+    return FALSE;
+
+  return TRUE;
+}
+
+GtkCssValue *
+_gtk_css_text_decoration_line_value_new (GtkTextDecorationLine line)
+{
+  GtkCssValue *value;
+
+  if (!text_decoration_line_is_valid (line))
+    return NULL;
+
+  value = _gtk_css_value_new (GtkCssValue, &GTK_CSS_VALUE_TEXT_DECORATION_LINE);
+  value->value = line;
+  value->name = NULL;
+  value->is_computed = TRUE;
+
+  return value;
+}
+
+GtkTextDecorationLine
+_gtk_css_text_decoration_line_try_parse_one (GtkCssParser          *parser,
+                                             GtkTextDecorationLine  base)
+{
+  guint i;
+  GtkTextDecorationLine value = 0;
+
+  g_return_val_if_fail (parser != NULL, 0);
+
+  for (i = 0; i < G_N_ELEMENTS (text_decoration_line_values); i++)
+    {
+      if (gtk_css_parser_try_ident (parser, text_decoration_line_values[i].name))
+        {
+          value = text_decoration_line_values[i].value;
+          break;
+        }
+    }
+
+  if (value == 0)
+    return base; /* not parsing this value */
+
+  if ((base | value) == base)
+    return 0; /* repeated value */
+
+  if (!text_decoration_line_is_valid (base | value))
+    return 0; /* bad combination */
+
+  return base | value;
+}
+
+GtkTextDecorationLine
+_gtk_css_text_decoration_line_value_get (const GtkCssValue *value)
+{
+  g_return_val_if_fail (value->class == &GTK_CSS_VALUE_TEXT_DECORATION_LINE, GTK_CSS_TEXT_DECORATION_LINE_NONE);
+
+  return value->value;
+}
+
 /* GtkCssFontVariantLigature */
 
 static FlagsValue font_variant_ligature_values[] = {
index 7ac181b7a9beed241e4cf86a1a3956bfafdfdea0..a102a8650b3fe910db8bc72badd0effb134b898e 100644 (file)
@@ -54,11 +54,12 @@ GtkCssValue *   _gtk_css_font_stretch_value_new       (PangoStretch       stretc
 GtkCssValue *   _gtk_css_font_stretch_value_try_parse (GtkCssParser      *parser);
 PangoStretch    _gtk_css_font_stretch_value_get       (const GtkCssValue *value);
 
-GtkCssValue *         _gtk_css_text_decoration_line_value_new       (GtkTextDecorationLine  line);
-GtkCssValue *         _gtk_css_text_decoration_line_value_try_parse (GtkCssParser          *parser);
-GtkTextDecorationLine _gtk_css_text_decoration_line_value_get       (const GtkCssValue     *value);
+GtkCssValue *         _gtk_css_text_decoration_line_value_new     (GtkTextDecorationLine  line);
+GtkTextDecorationLine _gtk_css_text_decoration_line_try_parse_one (GtkCssParser          *parser,
+                                                                   GtkTextDecorationLine  base);
+GtkTextDecorationLine _gtk_css_text_decoration_line_value_get     (const GtkCssValue     *value);
 
-GtkCssValue *          _gtk_css_text_decoration_style_value_new       (GtkTextDecorationStyle  style);
+GtkCssValue *          _gtk_css_text_decoration_style_value_new   (GtkTextDecorationStyle  style);
 GtkCssValue *          _gtk_css_text_decoration_style_value_try_parse (GtkCssParser           *parser);
 GtkTextDecorationStyle _gtk_css_text_decoration_style_value_get       (const GtkCssValue      *value);
 
index 4a9b97fd07ba215a21a83cfc8c4acb93b4872682..5908904d25596ef91eb7c53efd7bc4aca09e7fe5 100644 (file)
@@ -865,16 +865,26 @@ parse_text_decoration (GtkCssShorthandProperty  *shorthand,
                        GtkCssValue             **values,
                        GtkCssParser             *parser)
 {
+  GtkTextDecorationLine line = 0;
+
   do
   {
-    if (values[0] == NULL &&
-        (values[0] = _gtk_css_text_decoration_line_value_try_parse (parser)))
+    GtkTextDecorationLine parsed_line;
+
+    parsed_line = _gtk_css_text_decoration_line_try_parse_one (parser, line);
+
+    if (parsed_line == 0 && line != 0)
       {
-        if (values[0] == NULL)
-          return FALSE;
+        gtk_css_parser_error_value (parser, "Invalid combination of text-decoration-line values");
+        return FALSE;
+      }
+
+    if (parsed_line != line)
+      {
+        line = parsed_line;
       }
     else if (values[1] == NULL &&
-        (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
+             (values[1] = _gtk_css_text_decoration_style_value_try_parse (parser)))
       {
         if (values[1] == NULL)
           return FALSE;
@@ -895,6 +905,16 @@ parse_text_decoration (GtkCssShorthandProperty  *shorthand,
   }
   while (!value_is_done_parsing (parser));
 
+  if (line != 0)
+    {
+      values[0] = _gtk_css_text_decoration_line_value_new (line);
+      if (values[0] == NULL)
+        {
+          gtk_css_parser_error_value (parser, "Invalid combination of text-decoration-line values");
+          return FALSE;
+        }
+    }
+
   return TRUE;
 }
 
index ee6f9c44c56468b3cadb2d37f1886a484e915a3c..91830266a59df5ccedcc33627268df708e685ddc 100644 (file)
@@ -451,32 +451,29 @@ gtk_css_style_get_pango_attributes (GtkCssStyle *style)
                                                    ? style->font_variant->text_decoration_color
                                                    : style->core->color);
 
-  switch (decoration_line)
+  if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE)
     {
-    case GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE:
       attrs = add_pango_attr (attrs, pango_attr_underline_new (get_pango_underline_from_style (decoration_style)));
       if (!gdk_rgba_equal (color, decoration_color))
         attrs = add_pango_attr (attrs, pango_attr_underline_color_new (decoration_color->red * 65535. + 0.5,
                                                                        decoration_color->green * 65535. + 0.5,
                                                                        decoration_color->blue * 65535. + 0.5));
-      break;
-    case GTK_CSS_TEXT_DECORATION_LINE_OVERLINE:
+    }
+  if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_OVERLINE)
+    {
       attrs = add_pango_attr (attrs, pango_attr_overline_new (get_pango_overline_from_style (decoration_style)));
       if (!gdk_rgba_equal (color, decoration_color))
         attrs = add_pango_attr (attrs, pango_attr_overline_color_new (decoration_color->red * 65535. + 0.5,
                                                                       decoration_color->green * 65535. + 0.5,
                                                                       decoration_color->blue * 65535. + 0.5));
-      break;
-    case GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH:
+    }
+  if (decoration_line & GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH)
+    {
       attrs = add_pango_attr (attrs, pango_attr_strikethrough_new (TRUE));
       if (!gdk_rgba_equal (color, decoration_color))
         attrs = add_pango_attr (attrs, pango_attr_strikethrough_color_new (decoration_color->red * 65535. + 0.5,
                                                                            decoration_color->green * 65535. + 0.5,
                                                                            decoration_color->blue * 65535. + 0.5));
-      break;
-    case GTK_CSS_TEXT_DECORATION_LINE_NONE:
-    default:
-      break;
     }
 
   /* letter-spacing */
index 3eb4ac7c5ce906e546a3e0bc6d05c7b3b4ff94c5..8e5d4bf79674fea37c6db444bbd580b500f9c726 100644 (file)
@@ -317,14 +317,38 @@ parse_letter_spacing (GtkCssStyleProperty *property,
   return _gtk_css_number_value_parse (parser, GTK_CSS_PARSE_LENGTH);
 }
 
+static gboolean
+value_is_done_parsing (GtkCssParser *parser)
+{
+  return gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF) ||
+         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA) ||
+         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SEMICOLON) ||
+         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY);
+}
+
 static GtkCssValue *
 parse_text_decoration_line (GtkCssStyleProperty *property,
                             GtkCssParser        *parser)
 {
-  GtkCssValue *value = _gtk_css_text_decoration_line_value_try_parse (parser);
+  GtkCssValue *value = NULL;
+  GtkTextDecorationLine line;
+
+  line = 0;
+  do {
+    GtkTextDecorationLine parsed;
+
+    parsed = _gtk_css_text_decoration_line_try_parse_one (parser, line);
+    if (parsed == 0 || parsed == line)
+      {
+        gtk_css_parser_error_syntax (parser, "Not a valid value");
+        return NULL;
+      }
+    line = parsed;
+  } while (!value_is_done_parsing (parser));
 
+  value = _gtk_css_text_decoration_line_value_new (line);
   if (value == NULL)
-    gtk_css_parser_error_syntax (parser, "unknown text decoration line value");
+    gtk_css_parser_error_syntax (parser, "Invalid combination of values");
 
   return value;
 }
@@ -353,15 +377,6 @@ parse_font_kerning (GtkCssStyleProperty *property,
   return value;
 }
 
-static gboolean
-value_is_done_parsing (GtkCssParser *parser)
-{
-  return gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_EOF) ||
-         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_COMMA) ||
-         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_SEMICOLON) ||
-         gtk_css_parser_has_token (parser, GTK_CSS_TOKEN_CLOSE_CURLY);
-}
-
 static GtkCssValue *
 parse_font_variant_ligatures (GtkCssStyleProperty *property,
                               GtkCssParser        *parser)
index efb54a5561d6c937e3a733246e20d31a13ca0451..35ae56a41c5bb4692e28def1a7f4b8ae3a2c0a00 100644 (file)
@@ -325,10 +325,10 @@ typedef enum /*< skip >*/ {
 } GtkCssFontSize;
 
 typedef enum /*< skip >*/ {
-  GTK_CSS_TEXT_DECORATION_LINE_NONE,
-  GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE,
-  GTK_CSS_TEXT_DECORATION_LINE_OVERLINE,
-  GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH
+  GTK_CSS_TEXT_DECORATION_LINE_NONE         = 1 << 0,
+  GTK_CSS_TEXT_DECORATION_LINE_UNDERLINE    = 1 << 1,
+  GTK_CSS_TEXT_DECORATION_LINE_OVERLINE     = 1 << 2,
+  GTK_CSS_TEXT_DECORATION_LINE_LINE_THROUGH = 1 << 3
 } GtkTextDecorationLine;
 
 typedef enum /*< skip >*/ {
index f70753c2b591e3c309b5016ba3b712796f95f48b..b39825145064b07cc238f7f61e8032e61ce326a2 100644 (file)
@@ -423,6 +423,8 @@ test_data = [
   'string-values.css',
   'string-values.ref.css',
   'test.png',
+  'text-decoration.css',
+  'text-decoration.ref.css',
   'text-decoration-color.css',
   'text-decoration-color.ref.css',
   'text-decoration-line.css',
index 16311eb23c74aa800d69601f125dffdc1c32c798..1725688a8c6c2fb12c83f0f232fb4b9ea5523ca1 100644 (file)
@@ -21,3 +21,11 @@ e {
 f {
   text-decoration-line: line-through;
 }
+
+g {
+  text-decoration-line: overline;
+}
+
+h {
+  text-decoration-line: underline overline;
+}
index 16311eb23c74aa800d69601f125dffdc1c32c798..1725688a8c6c2fb12c83f0f232fb4b9ea5523ca1 100644 (file)
@@ -21,3 +21,11 @@ e {
 f {
   text-decoration-line: line-through;
 }
+
+g {
+  text-decoration-line: overline;
+}
+
+h {
+  text-decoration-line: underline overline;
+}
diff --git a/testsuite/css/parser/text-decoration.css b/testsuite/css/parser/text-decoration.css
new file mode 100644 (file)
index 0000000..3496b07
--- /dev/null
@@ -0,0 +1,23 @@
+a {
+  text-decoration: underline;
+}
+
+b {
+  text-decoration: wavy;
+}
+
+c {
+  text-decoration: red;
+}
+
+d {
+  text-decoration: red wavy;
+}
+
+e {
+  text-decoration: none;
+}
+
+f {
+  text-decoration: red underline wavy overline;
+}
diff --git a/testsuite/css/parser/text-decoration.ref.css b/testsuite/css/parser/text-decoration.ref.css
new file mode 100644 (file)
index 0000000..7369332
--- /dev/null
@@ -0,0 +1,35 @@
+a {
+  text-decoration-color: initial;
+  text-decoration-line: underline;
+  text-decoration-style: initial;
+}
+
+b {
+  text-decoration-color: initial;
+  text-decoration-line: initial;
+  text-decoration-style: wavy;
+}
+
+c {
+  text-decoration-color: rgb(255,0,0);
+  text-decoration-line: initial;
+  text-decoration-style: initial;
+}
+
+d {
+  text-decoration-color: rgb(255,0,0);
+  text-decoration-line: initial;
+  text-decoration-style: wavy;
+}
+
+e {
+  text-decoration-color: initial;
+  text-decoration-line: none;
+  text-decoration-style: initial;
+}
+
+f {
+  text-decoration-color: rgb(255,0,0);
+  text-decoration-line: underline overline;
+  text-decoration-style: wavy;
+}